#pragma once

#include <windows.h>
#include <list>
#include <boost/thread/mutex.hpp>

//#include "ThreadMemoryAllocator.h"


namespace platform {




/**
 * The list is designed to be used for per-thread
 * message queues, and it takes ownership over the
 * elements, passed by a pointer, and later passes
 * the ownership to consumer.
 *
 * Assumes that only a single thread at a time can
 * add messages, and a single thread can read messages
 * (but they can do it sumiltaneously).
 */
template<	typename T,
			int NUMBER_OF_NEW_ENTRIES_TO_SIGNAL = 50,
			bool RELEASE_MEMORY_ON_EXIT = true
		>
class SafeList
{
private:
	// A single node, allocated from thread-specific heap
	struct SingleDirectionNode {
		T * pObj;
		SingleDirectionNode * pNext;

		inline SingleDirectionNode(T * t) : pObj(t), pNext(NULL) {}
		inline ~SingleDirectionNode(void) {}

//		static void * operator new(unsigned int size) { return ThreadMemoryAllocator::Allocate(size); }
//		static void operator delete(void *p) { ThreadMemoryAllocator::Free(p); }
	};


public:
	/**
	 * Helper class, used to handle orphan
	 * elements on list's destruction
	 */
	class OrphanElementsHandler : public std::list<T*> {
		boost::mutex m_mutex;

	public:
		inline boost::mutex & getMutex(void) { return m_mutex; }
	};

public:
	inline SafeList(void) : m_first(NULL), m_last(NULL), m_unsignaledEntries(0), m_orphansHandler(NULL)
	{ m_event = ::CreateEvent(NULL, FALSE, FALSE, NULL); }

	inline void flushOrphans(OrphanElementsHandler *handler) {
		if (handler != NULL) {
			boost::mutex::scoped_lock lock(handler->getMutex());
			SingleDirectionNode *tmp = m_first;
			while (tmp != NULL) {
				handler->push_back(tmp->pObj);
				tmp = tmp->pNext;
			}
		}
	}

	inline void flushOrphans(void) { flushOrphans(m_orphansHandler); }

	inline ~SafeList(void)	{
		flushOrphans();

		m_last = NULL;
		if (RELEASE_MEMORY_ON_EXIT) {
			while (m_first != NULL) {
				SingleDirectionNode *tmp = m_first->pNext;
				delete m_first;
				m_first = tmp;
			}
		}

		if (m_event != NULL)
			::CloseHandle(m_event);
	}

public:
	inline void push_back(T * t) {
		SingleDirectionNode *pNew = new SingleDirectionNode(t);
		if (m_last != NULL) m_last->pNext = pNew;
		m_last = pNew;
		if (m_first == NULL)
			m_first = pNew;

		if (++m_unsignaledEntries == NUMBER_OF_NEW_ENTRIES_TO_SIGNAL) {
			// that's time to signal
			m_unsignaledEntries = 0;
			::SetEvent(m_event);
		}
	}

	inline T * pop_front(void) {
		if (m_first == NULL || m_first == m_last)
			return NULL;
		T * t = m_first->pObj;
		m_first->pObj = NULL;
		SingleDirectionNode *tmp = m_first->pNext;
		delete m_first;
		m_first = tmp;
		if (m_unsignaledEntries > 0) m_unsignaledEntries--;
		return t;
	}


	inline HANDLE getWaitEvent(void) { return m_event; }
	inline HANDLE stealWaitEvent(void) { HANDLE h = m_event; m_event = NULL; return h; }

	inline void setOrphanElementsHandler(OrphanElementsHandler *h) { m_orphansHandler = h; }


private:
	SingleDirectionNode * m_first;
	SingleDirectionNode * m_last;

	HANDLE m_event;
	int m_unsignaledEntries;

	OrphanElementsHandler *m_orphansHandler;
};

}  // end namespace
